Multi-currency: FX conversion and reporting currency - #56
Merged
Merged
Conversation
ItsThompson
force-pushed
the
mc/04-fx-conversion-legacy-reporting
branch
from
August 15, 2026 22:49
228597a to
91a8e19
Compare
ItsThompson
force-pushed
the
mc/04-fx-conversion-legacy-reporting
branch
from
August 15, 2026 22:55
91a8e19 to
83ef524
Compare
ItsThompson
force-pushed
the
mc/04-fx-conversion-legacy-reporting
branch
from
August 15, 2026 23:10
83ef524 to
da072b0
Compare
ItsThompson
force-pushed
the
mc/04-fx-conversion-legacy-reporting
branch
from
August 15, 2026 23:53
402e662 to
2d68e9b
Compare
ItsThompson
force-pushed
the
mc/04-fx-conversion-legacy-reporting
branch
from
August 16, 2026 14:52
2d68e9b to
acd9ed1
Compare
ItsThompson
force-pushed
the
mc/04-fx-conversion-legacy-reporting
branch
from
August 16, 2026 15:17
acd9ed1 to
f548caf
Compare
ItsThompson
force-pushed
the
mc/04-fx-conversion-legacy-reporting
branch
2 times, most recently
from
August 16, 2026 18:45
3e6fa9a to
ea2bf5f
Compare
ItsThompson
force-pushed
the
mc/04-fx-conversion-legacy-reporting
branch
from
August 18, 2026 23:36
ea2bf5f to
002148a
Compare
ItsThompson
force-pushed
the
mc/04-fx-conversion-legacy-reporting
branch
2 times, most recently
from
August 18, 2026 23:51
9bc77ae to
60d4235
Compare
ItsThompson
force-pushed
the
mc/04-fx-conversion-legacy-reporting
branch
from
August 19, 2026 06:30
60d4235 to
89e07fb
Compare
ItsThompson
force-pushed
the
mc/04-fx-conversion-legacy-reporting
branch
3 times, most recently
from
August 20, 2026 14:58
dd99103 to
7f1e87f
Compare
…ency across read paths Move the service-layer legacy snapshot resolution out of the FX-wiring commit. Every read path (expense list, detail, correction history, pro-rata group, and export stream) resolves repository-synthesized legacy rows to the period reporting currency and emits normalization telemetry, without failing the read when period context is unavailable.
…mount sort and currency filters
- gofmt suggestions model/service files - rename misleading legacy-suggestion test - name and document the reporting-currency fallback - label dual amounts as budget impact for accessibility - scope mobile mixed-currency test to the mobile list - make canonical suggestion fields optional so fallbacks are meaningful - add foreign-suggestion-currency integration test
- Collapse mapFxError to a single safe CONVERSION_UNAVAILABLE mapping; the dead gRPC-code switch implied distinctions that do not exist. - Normalize the period reporting currency once in CreateExpense and use it for validation, the identity-vs-FX decision, snapshots, and the FX target. - Require a non-nil FxClient: NewExpenseService panics on nil instead of leaking a test-only runtime fallback; tests use a loud stubFxClient. - Return and close the FX gRPC connection in main.go, matching the finance client pattern. - Drop unused FxConvertResponse echo fields and use the shared exchange source constant in tests. - Assert RequestedAt in FX success tests via a fixed clock seam.
Spec 05 requires Expense to preserve FX error categories rather than collapse every gRPC status into 503. mapFxError now inspects status.Code: - Unavailable/FailedPrecondition -> 503 CONVERSION_UNAVAILABLE - InvalidArgument UNSUPPORTED_CURRENCY -> 400 UNSUPPORTED_CURRENCY - InvalidArgument INVALID_AMOUNT -> 400 VALIDATION_ERROR - Internal/unclassified -> 500 INTERNAL_SERVER_ERROR (reported) - non-gRPC transport failure -> 503 CONVERSION_UNAVAILABLE Also address review-7 nits: report an unsupported period reporting currency as a 500 internal invariant violation (not a retryable 503), rename the misnamed FX-client-unavailable test, and replace the always-true repo matcher with mock.AnythingOfType.
…g currency first - Log non-503 FX failures (Internal/unclassified) as Error with event foreign_currency_conversion_failed instead of the misleading foreign_currency_conversion_unavailable Info event. - Validate the period reporting currency before resolving the transaction currency so a corrupted reporting currency surfaces as a 500 internal invariant violation in every branch, including the no-currency-fields defaulting path, instead of a 400 transaction-currency error. - Add tests for both behaviors.
- Remove LegacySynthesized/PartialSnapshotFields and service-layer legacy normalization; rely on InitSchema backfill and version-0 error. - Read suggestion transaction columns directly without the legacy amount/currency fallback; keep version-1 integrity telemetry. - Update finance tests for the removed reporting-amount fallback and read the shared currency catalog from code.
- Make transactionAmount, reportingAmount, and reportingCurrency required and read them directly without legacy fallbacks. - Update finance and shell fixtures/mocks to supply the required snapshot fields. - Fix shell mock reporting-currency fields for comparison, health score, and trends.
ItsThompson
force-pushed
the
mc/04-fx-conversion-legacy-reporting
branch
from
August 20, 2026 16:04
34e0698 to
5eeafb3
Compare
ItsThompson
commented
Aug 20, 2026
…ntegrity telemetry
…ount/transaction_currency
The suggestions API no longer returns the deprecated amount/currency aliases, so the autocomplete smoke test asserts transactionAmount.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Implements FX-backed foreign-currency expense creation, mixed-currency expense suggestions and log, and reporting-currency authority across dashboard/history/health-score. Period reporting currency is the single source of truth for money formatting and computation. FX conversion safe-fails on provider outages: no ledger row is written and the form stays ready for retry. Legacy rows are migrated by mc/03 startup backfill; this PR adds integrity telemetry for rows missing required snapshot fields.
What changed
Backend (Go)
fx_client.go,expense.go,config/): Injected FX gRPC client viaFX_SERVICE_ADDR. Create-expense validates currencies, callsConvertAmountwhen transaction != reporting currency, and writes both money snapshots. FX failures map toCONVERSION_UNAVAILABLE(503) with no ledger write.model/suggestions.go,service/suggestions.go,repository/immudb.go): Suggestion models include canonical transaction amount and currency; repository selects and mapstransaction_amount/transaction_currencydirectly.healthscore_service.go,healthscore.go,healthscore_trend.go):computeHealthScoreusesperiod.ReportingCurrencyinstead of default settings.FormulaVersionbumped 2 -> 3. Stored scores get backfilled reporting currency. Trend points carry per-period reporting currency.dashboard.go,model/requests.go): Spending trends and historical comparison carry per-point reporting currency.HistoricalComparisonaddsPreviousReportingCurrencyandComparable; cross-currency pairs suppress change percent and rolling average.000006_add_period_reporting_currency.*.sql): Three-step backfill (default settings currency, auth user currency, app fallback). Validates every row has a supported currency before addingNOT NULL+ CHECK constraint.repository/immudb.go): Rows missing required snapshot fields return a typedSnapshotIntegrityErrorand emitexpense_snapshot_integrity_errortelemetry.Frontend (TypeScript/React)
expense-table-columns.tsx): Mixed-currency rows show transaction amount and secondary reporting amount labeled "Budget impact". Amount column sorts by reporting amount. Transaction and reporting currency filters are distinct.row.period.reportingCurrencyinstead ofuser.currency. Adjacent rows with different currencies show "Δ not comparable (different currency)".comparable. Recent expenses usereportingAmount.reportingCurrencytoHealthScore,HealthScoreTrendPoint, andTrendPoint; addedpreviousReportingCurrencyandcomparabletoHistoricalComparison; madetransactionAmount,reportingAmount, andreportingCurrencyrequired onExpense.Tests
All suites pass: expense, finance, fx, and frontend (finance 53 files/509 tests, core 88, api 187).
gofmtandgo vetclean.Notes
000006hardcodesUSDas the app fallback currency; a configurable fallback can be a follow-up.SpendingTrendChartuses a single period currency for Y-axis formatting; mixed-currency trend-axis normalization is a candidate follow-up.